package com.ybear.ybutils.utils

import android.content.Context
import android.os.Build
import android.text.Editable
import android.text.InputFilter
import android.text.Layout
import android.text.SpannableString
import android.text.TextWatcher
import android.text.style.AlignmentSpan
import android.text.style.LeadingMarginSpan
import android.widget.EditText
import android.widget.TextView
import androidx.annotation.ArrayRes
import androidx.annotation.RequiresApi
import com.ybear.ybutils.utils.log.LogUtil
import java.math.BigDecimal
import java.math.RoundingMode
import java.text.DecimalFormat
import java.text.DecimalFormatSymbols
import java.util.Locale
import java.util.regex.Pattern
import kotlin.math.log
import kotlin.math.max
import kotlin.math.min
import kotlin.math.pow

/**
 * 对象工具类
 * @author MiWi
 */
object ObjUtils {
    @JvmStatic
    private val mDefShorthandUnit = arrayOf( "", "K", "M", "B" )
    private const val PERIOD = '.'

    @JvmStatic
    private fun getUnit(
        context: Context?, @ArrayRes unitArrResId: Int, vararg unit: String
    ): Array<out String> {
        return context?.resources?.getStringArray( unitArrResId ) ?: unit
    }

    /**
     * 数值格式化
     * @param locale Locale，默认值为当前语言环境
     * @param format 格式样式，eg:  ##   or  ###
     * @param num 数值，eg: 123456789  or  123456789.23
     * @param decimalCount 保留小数位数，eg:   0   or    2
     * @param mode 四舍五入模式，eg: [RoundingMode.DOWN]
     * @return 格式化结果，eg:  12   or  123.45
     */
    @JvmStatic
    fun toDecimalFormat(
        locale: Locale = LocaleUtil.getLocale(),
        format: String,
        num: Double,
        decimalCount: Int,
        mode: RoundingMode
    ): String {
        return try {
            val sb = StringBuilder( format )
            if ( decimalCount > 0 ) {
                sb.append( "." )
                repeat( decimalCount ) { sb.append( "0" ) }
            }
            val df = DecimalFormat.getInstance( locale ) as DecimalFormat
            df.roundingMode = mode
            df.decimalFormatSymbols = DecimalFormatSymbols.getInstance( locale )
            df.applyPattern( sb.toString() )
            df.format( BigDecimal.valueOf( num ) )
        } catch ( e: Exception ) {
            e.printStackTrace()
            num.toString() // 使用默认转换，避免异常
        }
    }

    /**
     * 数值格式化（使用默认语言环境）
     * @param format 格式样式，eg:  ##   or  ###
     * @param num 数值，eg: 123456789  or  123456789.23
     * @param decimalCount 保留小数位数，eg:   0   or    2
     * @param mode 四舍五入模式，eg: [RoundingMode.DOWN]
     * @return 格式化结果，eg:  12   or  123.45
     */
    @JvmStatic
    fun toDecimalFormat(
        format: String,
        num: Double,
        decimalCount: Int,
        mode: RoundingMode
    ): String {
        return toDecimalFormat( LocaleUtil.getLocale(), format, num, decimalCount, mode )
    }

    /**
     * 数值格式化（保留两位小数，使用默认语言环境）
     * @param format 格式样式，eg:  ##   or  ###
     * @param num 数值，eg: 123456789  or  123456789.23
     * @param mode 四舍五入模式，eg: [RoundingMode.DOWN]
     * @return 格式化结果，eg:  12   or  123.45
     */
    @JvmStatic
    fun toDecimalFormat(format: String, num: Double, mode: RoundingMode): String {
        return toDecimalFormat( format, num, 2, mode )
    }

    /**
     * 数值格式化（保留两位小数，使用默认语言环境，默认四舍五入模式为 RoundingMode.DOWN）
     * @param num 数值，eg: 123456789  or  123456789.23
     * @param mode 四舍五入模式，eg: [RoundingMode.DOWN]
     * @return 格式化结果，eg:  12   or  123.45
     */
    @JvmStatic
    fun toDecimalFormat(num: Double, mode: RoundingMode): String {
        return toDecimalFormat( "###", num, mode )
    }

    /**
     * 数值格式化 (保留两位小数，使用默认语言环境，默认四舍五入模式为RoundingMode.DOWN)
     * @param num 数值，eg: 123456789  or  123456789.23
     * @param decimalCount 保留小数位数，eg:   0   or    2
     * @return 格式化结果，eg:  12   or  123.45
     */
    @JvmStatic
    fun toDecimalFormat(num: Double, decimalCount: Int): String {
        return toDecimalFormat( "###", num, decimalCount, RoundingMode.DOWN )
    }

    /**
     * 数值格式化（不允许四舍五入，使用默认语言环境）
     * @param locale Locale，默认值为当前语言环境
     * @param format 格式样式，eg:  ##   or  ###
     * @param num 数值，eg: 123456789  or  123456789.23
     * @param decimalCount 保留小数位数，eg:   0   or    2
     * @return 格式化结果，eg:  12   or  123.45
     */
    @JvmStatic
    fun toDecimalFormat(
        locale: Locale = LocaleUtil.getLocale(), format: String, num: Double, decimalCount: Int
    ): String {
        return toDecimalFormat( locale, format, num, decimalCount, RoundingMode.DOWN )
    }

    /**
     * 数值格式化（不允许四舍五入，使用默认格式样式 "###"）
     * @param locale Locale，默认值为当前语言环境
     * @param num 数值，eg: 123456789  or  123456789.23
     * @param decimalCount 保留小数位数，eg:   0   or    2
     * @param mode 四舍五入模式，eg: [RoundingMode.DOWN]
     * @return 格式化结果，eg:  12   or  123.45
     */
    @JvmStatic
    fun toDecimalFormat(
        locale: Locale = LocaleUtil.getLocale(), num: Double, decimalCount: Int, mode: RoundingMode
    ): String {
        return toDecimalFormat( locale, "###", num, decimalCount, mode )
    }

    /**
     * 数值格式化（不允许四舍五入，使用默认语言环境）
     * @param format 格式样式，eg:  ##   or  ###
     * @param num 数值，eg: 123456789  or  123456789.23
     * @param decimalCount 保留小数位数，eg:   0   or    2
     * @return 格式化结果，eg:  12   or  123.45
     */
    @JvmStatic
    fun toDecimalFormat(format: String, num: Double, decimalCount: Int): String {
        return toDecimalFormat( format, num, decimalCount, RoundingMode.DOWN )
    }

    /**
     * 数值格式化（不允许四舍五入，使用默认格式样式 "###", 默认四舍五入模式为 RoundingMode.DOWN）
     * @param locale Locale，默认值为当前语言环境
     * @param num 数值，eg: 123456789  or  123456789.23
     * @param decimalCount 保留小数位数，eg:   0   or    2
     * @return 格式化结果，eg:  12   or  123.45
     */
    @JvmStatic
    fun toDecimalFormat(
        locale: Locale = LocaleUtil.getLocale(), num: Double, decimalCount: Int
    ): String {
        return toDecimalFormat( locale, num, decimalCount, RoundingMode.DOWN )
    }

    /**
     * 数值格式化（不允许四舍五入，保留0位小数，使用默认语言环境）
     * @param locale Locale，默认值为当前语言环境
     * @param format 格式样式，eg:  ##   or  ###
     * @param num 数值，eg: 123456789  or  123456789.23
     * @return 格式化结果，eg:  12   or  123.45
     */
    @JvmStatic
    fun toDecimalFormat(
        locale: Locale = LocaleUtil.getLocale(), format: String, num: Double
    ): String {
        return toDecimalFormat( locale, format, num, 0 )
    }

    /**
     * 数值格式化（不允许四舍五入，保留0位小数，使用默认语言环境，使用默认格式样式 "###"）
     * @param locale Locale，默认值为当前语言环境
     * @param num 数值，eg: 123456789  or  123456789.23
     * @return 格式化结果，eg:  12   or  123.45
     */
    @JvmStatic
    fun toDecimalFormat(locale: Locale = LocaleUtil.getLocale(), num: Double): String {
        return toDecimalFormat( locale, num, 0 )
    }

    /**
     * 数值格式化（不允许四舍五入，保留0位小数，使用默认语言环境）
     * @param format 格式样式，eg:  ##   or  ###
     * @param num 数值，eg: 123456789  or  123456789.23
     * @return 格式化结果，eg:  12   or  123.45
     */
    @JvmStatic
    fun toDecimalFormat(format: String, num: Double): String {
        return toDecimalFormat( LocaleUtil.getLocale(), format, num )
    }

    /**
     * 数值格式化（不允许四舍五入，保留0位小数，使用默认语言环境，使用默认格式样式 "###"）
     * @param num 数值，eg: 123456789  or  123456789.23
     * @return 格式化结果，eg:  12   or  123.45
     */
    @JvmStatic
    fun toDecimalFormat(num: Double): String {
        return toDecimalFormat( LocaleUtil.getLocale(), num )
    }


    /**
     * 货币小数格式化
     * @param locale Locale，默认值为当前语言环境
     * @param num 数值，eg: 123456789  or 123456789.23
     * @param decimalCount 保留小数位数，eg:  0  or    2
     * @return 格式化结果，eg: 123,456,789  or  123,456,789.23
     */
    @JvmStatic
    fun toPriceDecimal(
        locale: Locale = LocaleUtil.getLocale(), num: Double, decimalCount: Int
    ): String {
        return toDecimalFormat( locale, "#,###", num, decimalCount )
    }

    /**
     * 货币小数格式化（保留0位小数）
     * @param locale Locale，默认值为当前语言环境
     * @param num 数值，eg: 123456789
     * @return 格式化结果，eg: 123,456,789
     */
    @JvmStatic
    fun toPriceDecimal(locale: Locale = LocaleUtil.getLocale(), num: Double): String {
        return toPriceDecimal( locale, num, 0 )
    }

    /**
     * 货币小数格式化（使用默认语言环境）
     * @param num 数值，eg: 123456789  or 123456789.23
     * @param decimalCount 保留小数位数，eg:  0  or    2
     * @return 格式化结果，eg: 123,456,789  or  123,456,789.23
     */
    @JvmStatic
    fun toPriceDecimal(num: Double, decimalCount: Int): String {
        return toPriceDecimal( LocaleUtil.getLocale(), num, decimalCount )
    }

    /**
     * 货币小数格式化（保留0位小数，使用默认语言环境）
     * @param num 数值，eg: 123456789
     * @return 格式化结果，eg: 123,456,789
     */
    @JvmStatic
    fun toPriceDecimal(num: Double): String {
        return toPriceDecimal( num, 0 )
    }

    /**
     * 数字缩写（分割）
     * @param context 上下文
     * @param locale Locale，默认值为当前语言环境
     * @param num 数值，eg: 10000
     * @param decimalCount 保留小数位数
     * @param disableRound 禁用四舍五入
     * @return 缩写结果，eg: ["9.8", "K"]
     */
    @JvmStatic
    fun toNumberSizeOfSplit(
        context: Context?,
        locale: Locale = LocaleUtil.getLocale(),
        num: Double,
        decimalCount: Int,
        disableRound: Boolean
    ): Array<String> {
        val result = toShorthand(
            locale,
            num,
            1000,
            decimalCount,
            disableRound,
            *getUnit( context, R.array.stringToNumberSize, *mDefShorthandUnit )
        )
        // eg：1.0, 2.00, 3.000... 返回 1, 2, 3
        val split = result[ 0 ].split( "\\.".toRegex() ).toTypedArray()
        if ( split.size >= 2 ) {
            result[ 0 ] = if ( split[ 1 ].toDouble() == 0.0 ) split[ 0 ] else result[ 0 ]
        }
        return result
    }

    /**
     * 数字缩写（分割，使用默认语言环境）
     * @param context 上下文
     * @param num 数值，eg: 10000
     * @param decimalCount 保留小数位数
     * @param disableRound 禁用四舍五入
     * @return 缩写结果，eg: ["9.8", "K"]
     */
    @JvmStatic
    fun toNumberSizeOfSplit(
        context: Context?,
        num: Double,
        decimalCount: Int,
        disableRound: Boolean
    ): Array<String> {
        return toNumberSizeOfSplit(
            context, LocaleUtil.getLocale(), num, decimalCount, disableRound
        )
    }

    /**
     * 数字缩写（分割，不禁用四舍五入，使用默认语言环境）
     * @param context 上下文
     * @param num 数值，eg: 10000
     * @param decimalCount 保留小数位数
     * @return 缩写结果，eg: ["9.8", "K"]
     */
    @JvmStatic
    fun toNumberSizeOfSplit(
        context: Context?, num: Double, decimalCount: Int
    ): Array<String> {
        return toNumberSizeOfSplit( context, num, decimalCount, false )
    }

    /**
     * 数字缩写（分割，保留一位小数，使用默认语言环境）
     * @param context 上下文
     * @param locale Locale，默认值为当前语言环境
     * @param num 数值，eg: 10000
     * @param disableRound 禁用四舍五入
     * @return 缩写结果，eg: ["9.8", "K"]
     */
    @JvmStatic
    fun toNumberSizeOfSplit(
        context: Context?,
        locale: Locale = LocaleUtil.getLocale(),
        num: Double,
        disableRound: Boolean
    ): Array<String> {
        return toNumberSizeOfSplit( context, locale, num, 1, disableRound )
    }

    /**
     * 数字缩写（分割，保留一位小数，不禁用四舍五入，使用默认语言环境）
     * @param context 上下文
     * @param locale Locale，默认值为当前语言环境
     * @param num 数值，eg: 10000
     * @return 缩写结果，eg: ["9.8", "K"]
     */
    @JvmStatic
    fun toNumberSizeOfSplit(
        context: Context?, locale: Locale = LocaleUtil.getLocale(), num: Double
    ): Array<String> {
        return toNumberSizeOfSplit( context, locale, num, false )
    }

    /**
     * 数字缩写（分割，保留一位小数，使用默认语言环境）
     * @param context 上下文
     * @param num 数值，eg: 10000
     * @param disableRound 禁用四舍五入
     * @return 缩写结果，eg: ["9.8", "K"]
     */
    @JvmStatic
    fun toNumberSizeOfSplit(
        context: Context?, num: Double, disableRound: Boolean
    ): Array<String> {
        return toNumberSizeOfSplit( context, num, 1, disableRound )
    }

    /**
     * 数字缩写（分割，保留一位小数，不禁用四舍五入，使用默认语言环境）
     * @param context 上下文
     * @param num 数值，eg: 10000
     * @return 缩写结果，eg: ["9.8", "K"]
     */
    @JvmStatic
    fun toNumberSizeOfSplit(
        context: Context?, num: Double
    ): Array<String> {
        return toNumberSizeOfSplit( context, num, false )
    }

    /**
     * 数字缩写
     * @param context 上下文
     * @param locale Locale，默认值为当前语言环境
     * @param num 数值，eg: 10000
     * @param decimalCount 保留小数位数
     * @param disableRound 禁用四舍五入
     * @return 缩写结果，eg: 9.77K
     */
    @JvmStatic
    fun toNumberSize(
        context: Context?,
        locale: Locale = LocaleUtil.getLocale(),
        num: Double,
        decimalCount: Int,
        disableRound: Boolean
    ): String {
        val result = toNumberSizeOfSplit( context, locale, num, decimalCount, disableRound )
        return result[ 0 ] + result[ 1 ]
    }

    /**
     * 数字缩写（使用默认语言环境）
     * @param context 上下文
     * @param num 数值，eg: 10000
     * @param decimalCount 保留小数位数
     * @param disableRound 禁用四舍五入
     * @return 缩写结果，eg: 9.77K
     */
    @JvmStatic
    fun toNumberSize(
        context: Context?,
        num: Double,
        decimalCount: Int,
        disableRound: Boolean
    ): String {
        return toNumberSize( context, LocaleUtil.getLocale(), num, decimalCount, disableRound )
    }

    /**
     * 数字缩写（不禁用四舍五入，使用默认语言环境）
     * @param context 上下文
     * @param num 数值，eg: 10000
     * @param decimalCount 保留小数位数
     * @return 缩写结果，eg: 9.77K
     */
    @JvmStatic
    fun toNumberSize(
        context: Context?, num: Double, decimalCount: Int
    ): String {
        return toNumberSize( context, num, decimalCount, false )
    }

    /**
     * 数字缩写（保留一位小数，使用默认语言环境）
     * @param context 上下文
     * @param locale Locale，默认值为当前语言环境
     * @param num 数值，eg: 10000
     * @param disableRound 禁用四舍五入
     * @return 缩写结果，eg: 9.77K
     */
    @JvmStatic
    fun toNumberSize(
        context: Context?,
        locale: Locale = LocaleUtil.getLocale(),
        num: Double,
        disableRound: Boolean
    ): String {
        return toNumberSize( context, locale, num, 1, disableRound )
    }

    /**
     * 数字缩写（保留一位小数，不禁用四舍五入，使用默认语言环境）
     * @param context 上下文
     * @param locale Locale，默认值为当前语言环境
     * @param num 数值，eg: 10000
     * @return 缩写结果，eg: 9.77K
     */
    @JvmStatic
    fun toNumberSize(
        context: Context?, locale: Locale = LocaleUtil.getLocale(), num: Double
    ): String {
        return toNumberSize( context, locale, num, false )
    }

    /**
     * 数字缩写（保留一位小数，不禁用四舍五入，使用默认语言环境）
     * @param context 上下文
     * @param num 数值，eg: 10000
     * @return 缩写结果，eg: 9.77K
     */
    @JvmStatic
    fun toNumberSize(context: Context?, num: Double): String {
        return toNumberSize( context, num, 1 )
    }

    /**
     * 容量缩写（分割）
     * @param context 上下文
     * @param locale Locale，默认值为当前语言环境
     * @param num 数值，eg: 10000
     * @param decimalCount 保留小数位数
     * @return 缩写结果，eg: ["9.77", "K"]
     */
    @JvmStatic
    fun toMemorySizeOfSplit(
        context: Context?,
        locale: Locale = LocaleUtil.getLocale(),
        num: Double,
        decimalCount: Int
    ): Array<String> {
        return toShorthand(
            locale,
            num,
            1024,
            decimalCount,
            *getUnit(
                context,
                R.array.stringToMemorySize,
                "B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB", "BB", "NB", "DB", "CB"
            )
        )
    }

    /**
     * 容量缩写（分割，使用默认语言环境）
     * @param context 上下文
     * @param num 数值，eg: 10000
     * @param decimalCount 保留小数位数
     * @return 缩写结果，eg: ["9.77", "K"]
     */
    @JvmStatic
    fun toMemorySizeOfSplit(
        context: Context?, num: Double, decimalCount: Int
    ): Array<String> {
        return toMemorySizeOfSplit( context, LocaleUtil.getLocale(), num, decimalCount )
    }

    /**
     * 容量缩写（分割，保留两位小数，使用默认语言环境）
     * @param context 上下文
     * @param locale Locale，默认值为当前语言环境
     * @param num 数值，eg: 10000
     * @return 缩写结果，eg: ["9.77", "K"]
     */
    @JvmStatic
    fun toMemorySizeOfSplit(
        context: Context?, locale: Locale = LocaleUtil.getLocale(), num: Double
    ): Array<String> {
        return toMemorySizeOfSplit( context, locale, num, 2 )
    }

    /**
     * 容量缩写（分割，保留两位小数，使用默认语言环境）
     * @param context 上下文
     * @param num 数值，eg: 10000
     * @return 缩写结果，eg: ["9.77", "K"]
     */
    @JvmStatic
    fun toMemorySizeOfSplit(context: Context?, num: Double): Array<String> {
        return toMemorySizeOfSplit( context, num, 2 )
    }

    /**
     * 容量缩写
     * @param context 上下文
     * @param locale Locale，默认值为当前语言环境
     * @param num 数值，eg: 10000
     * @param decimalCount 保留小数位数
     * @return 缩写结果，eg: 10.00KB
     */
    @JvmStatic
    fun toMemorySize(
        context: Context?,
        locale: Locale = LocaleUtil.getLocale(),
        num: Double,
        decimalCount: Int
    ): String {
        val result = toMemorySizeOfSplit( context, locale, num, decimalCount )
        return result[ 0 ] + result[ 1 ]
    }

    /**
     * 容量缩写（使用默认语言环境）
     * @param context 上下文
     * @param num 数值，eg: 10000
     * @param decimalCount 保留小数位数
     * @return 缩写结果，eg: 10.00KB
     */
    @JvmStatic
    fun toMemorySize(
        context: Context?, num: Double, decimalCount: Int
    ): String {
        return toMemorySize( context, LocaleUtil.getLocale(), num, decimalCount )
    }

    /**
     * 容量缩写（保留两位小数，使用默认语言环境）
     * @param context 上下文
     * @param locale Locale，默认值为当前语言环境
     * @param num 数值，eg: 10000
     * @return 缩写结果，eg: 9.77KB
     */
    @JvmStatic
    fun toMemorySize(
        context: Context?, locale: Locale = LocaleUtil.getLocale(), num: Double
    ): String {
        return toMemorySize( context, locale, num, 2 )
    }

    /**
     * 容量缩写（保留两位小数，使用默认语言环境）
     * @param context 上下文
     * @param num 数值，eg: 10000
     * @return 缩写结果，eg: 9.77KB
     */
    @JvmStatic
    fun toMemorySize(context: Context?, num: Double): String {
        return toMemorySize( context, num, 2 )
    }

    /**
     * 容量缩写 (Double)
     * @param context 上下文
     * @param locale Locale，默认值为当前语言环境
     * @param num 数值，eg: 10000
     * @param decimalCount 保留小数位数
     * @return 缩写结果，eg: 10.00
     */
    @JvmStatic
    fun toMemorySizeOfDouble(
        context: Context?,
        locale: Locale = LocaleUtil.getLocale(),
        num: Double,
        decimalCount: Int
    ): Double {
        return toMemorySizeOfSplit( context, locale, num, decimalCount )[ 0 ].toDouble()
    }

    /**
     * 容量缩写 (Double，使用默认语言环境)
     * @param context 上下文
     * @param num 数值，eg: 10000
     * @param decimalCount 保留小数位数
     * @return 缩写结果，eg: 10.00
     */
    @JvmStatic
    fun toMemorySizeOfDouble(
        context: Context?, num: Double, decimalCount: Int
    ): Double {
        return toMemorySizeOfDouble( context, LocaleUtil.getLocale(), num, decimalCount )
    }

    /**
     * 容量缩写 (Double，保留两位小数，使用默认语言环境)
     * @param context 上下文
     * @param locale Locale，默认值为当前语言环境
     * @param num 数值，eg: 10000
     * @return 缩写结果，eg: 10.00
     */
    @JvmStatic
    fun toMemorySizeOfDouble(
        context: Context?, locale: Locale = LocaleUtil.getLocale(), num: Double
    ): Double {
        return toMemorySizeOfDouble( context, locale, num, 2 )
    }

    /**
     * 容量缩写 (Double，保留两位小数，使用默认语言环境)
     * @param context 上下文
     * @param num 数值，eg: 10000
     * @return 缩写结果，eg: 10.00
     */
    @JvmStatic
    fun toMemorySizeOfDouble(context: Context?, num: Double): Double {
        return toMemorySizeOfDouble( context, num, 2 )
    }

    /**
     * 数量大小缩写
     * @param locale Locale，默认值为当前语言环境
     * @param num 数值
     * @param span 数值之间的跨度，eg：数字为1000，内存容量为1024
     * @param decimalCount 保留小数位数
     * @param disableRound 禁用四舍五入
     * @param unit 所有跨度的单位值
     * @return 结果，eg: ["10", "K"]
     */
    @JvmStatic
    fun toShorthand(
        locale: Locale = LocaleUtil.getLocale(),
        num: Double,
        span: Int,
        decimalCount: Int,
        disableRound: Boolean = false,
        vararg unit: String
    ): Array<String> {
        val units = if ( unit.isEmpty() ) mDefShorthandUnit else unit
        if ( num <= 0 || span < 0 ) return arrayOf( num.toString(), units[ 0 ] )
        val i = ( log( num, span.toDouble() ) ).toInt()
        val result = num / span.toDouble().pow( i.toDouble() )
        LogUtil.e(
            "TAG", "Memory -> " +
                    "result: $result, format: ${toDecimalFormat( locale, result, decimalCount )}"
        )
        return arrayOf(
            if ( disableRound ) toDecimalFormat( locale, result, decimalCount )
            else String.format( locale, "%." + decimalCount + "f", result ),
            units[ max( 0, min( i, units.size - 1 ) ) ]
        )
    }

    /**
     * 数量大小缩写（不禁用四舍五入）
     * @param locale Locale，默认值为当前语言环境
     * @param num 数值
     * @param span 数值之间的跨度，eg：数字为1000，内存容量为1024
     * @param decimalCount 保留小数位数
     * @param unit 所有跨度的单位值
     * @return 结果，eg: ["10", "K"]
     */
    @JvmStatic
    fun toShorthand(
        locale: Locale = LocaleUtil.getLocale(),
        num: Double,
        span: Int,
        decimalCount: Int,
        vararg unit: String
    ): Array<String> {
        return toShorthand( locale, num, span, decimalCount, false, *unit )
    }

    /**
     * 数量大小缩写（使用默认语言环境）
     * @param num 数值
     * @param span 数值之间的跨度，eg：数字为1000，内存容量为1024
     * @param decimalCount 保留小数位数
     * @param unit 所有跨度的单位值
     * @return 结果，eg: ["10", "K"]
     */
    @JvmStatic
    fun toShorthand(num: Double, span: Int, decimalCount: Int, vararg unit: String): Array<String> {
        return toShorthand( LocaleUtil.getLocale(), num, span, decimalCount, *unit )
    }

    /**
     * 数量大小缩写（保留0位小数，使用默认语言环境）
     * @param num 数值
     * @param span 数值之间的跨度，eg：数字为1000，内存容量为1024
     * @param unit 所有跨度的单位值
     * @return 结果，eg: ["10", "K"]
     */
    @JvmStatic
    fun toShorthand(num: Double, span: Int, vararg unit: String): Array<String> {
        return toShorthand( LocaleUtil.getLocale(), num, span, 0, *unit )
    }

    /**
     * 是否存在中文字符
     * @param s 处理的字符串
     * @return 结果
     */
    @JvmStatic
    fun isChineseChar(s: String): Boolean {
        return Pattern.compile( "[\\u4e00-\\u9fa5]" ).matcher( s ).find()
    }

    /**
     * 是否为英文字符
     * @param c 处理的字符
     * @return 结果
     */
    @JvmStatic
    fun isEnglishChar(c: Char): Boolean {
        return ( c in 'A'..'Z' ) || ( c in 'a'..'z' )
    }

    /**
     * 是否为英文字符串
     * @param s 处理的字符串
     * @return 结果
     */
    @JvmStatic
    fun isEnglishChar(s: String?): Boolean {
        if ( s.isNullOrEmpty() ) return false
        s.forEach { if ( !isEnglishChar( it ) ) return false }
        return true
    }

    /**
     * 是否为数字字符
     * @param c 处理的字符
     * @return 结果
     */
    @JvmStatic
    fun isNumber(c: Char): Boolean { return c in '0'..'9' }

    /**
     * 是否为数字字符串
     * @param s 处理的字符串
     * @return 结果
     */
    @JvmStatic
    fun isNumber(s: String?): Boolean {
        if ( s.isNullOrEmpty() ) return false
        s.forEach { if ( !isNumber( it ) ) return false }
        return true
    }

    /**
     * 是否为小数字符串
     * @param s 处理的字符串
     * @return 结果
     */
    @JvmStatic
    fun isDecimalNumber(s: String?): Boolean {
        if ( s.isNullOrEmpty() ) return false
        var periodCount = 0
        for ( it in s ) {
            if ( it == PERIOD ) {
                periodCount++
                continue
            }
            if ( !isNumber( it ) ) return false
        }
        return !s.startsWith( PERIOD.toString() ) && s.length > 1 && periodCount == 1
    }

    /**
     * 是否为数字或字母混合
     * @param s 处理的字符串
     * @return 结果
     */
    @JvmStatic
    fun isEnglishOrNumber(s: String?): Boolean {
        if ( s.isNullOrEmpty() ) return false
        s.forEach { if ( !isEnglishChar( it ) && !isNumber( it ) ) return false }
        return true
    }

    /**
     * 是否为数字与字母混合
     * @param s 处理的字符串
     * @param minEnglishCount 至少存在的英文数量
     * @param minNumberCount 至少存在的数字数量
     * @return 结果
     */
    @JvmStatic
    fun isEnglishAndNumber(s: String?, minEnglishCount: Int, minNumberCount: Int): Boolean {
        if ( s.isNullOrEmpty() ) return false
        var eng = 0
        var num = 0
        for ( it in s ) {
            if ( isEnglishChar( it ) ) eng++
            if ( isNumber( it ) ) num++
        }
        return ( eng >= minEnglishCount && num >= minNumberCount ) && eng + num == s.length
    }

    /**
     * 是否为数字与字母混合（字母和数字至少存在一个）
     * @param s 处理的字符串
     * @return 结果
     */
    @JvmStatic
    fun isEnglishAndNumber(s: String?): Boolean {
        return isEnglishAndNumber( s, 1, 1 )
    }

    /**
     * 对象转指定类型（Int, Double, String ...）
     * @param obj 待转换的对象
     * @return 返回 [ValueOf]， 通过 [ValueOf] 转换为指定类型
     */
    @JvmStatic
    fun valueOf(obj: Any?): ValueOf {
        return ValueOf.with( obj )
    }

    /**
     * 对象转 String
     * @param obj 待转换的对象
     * @return 转换后的字符串，如果对象为 null，则返回空字符串 ""
     */
    @JvmStatic
    fun parseString(obj: Any?): String {
        return valueOf(obj).toString()
    }

    /**
     * 对象转 Object
     * @param obj 待转换的对象
     * @return 转换后的对象，如果对象为 null，则返回 null
     */
    @JvmStatic
    fun parseObject(obj: Any?): Any? {
        return valueOf(obj).toObject()
    }

    /**
     * 对象转 Byte (指定进制)
     * @param obj 待转换的对象
     * @param radix 进制
     * @return 转换后的 Byte 值，如果转换失败，则返回 0
     */
    @JvmStatic
    fun parseByte(obj: Any?, radix: Int): Byte {
        return valueOf(obj).toByte(radix)
    }

    /**
     * 对象转 Byte (默认十进制)
     * @param obj 待转换的对象
     * @return 转换后的 Byte 值，如果转换失败，则返回 0
     */
    @JvmStatic
    fun parseByte(obj: Any?): Byte {
        return valueOf(obj).toByte()
    }

    /**
     * 对象转 Double
     * @param obj 待转换的对象
     * @return 转换后的 Double 值，如果转换失败，则返回 0.0
     */
    @JvmStatic
    fun parseDouble(obj: Any?): Double {
        return valueOf(obj).toDouble()
    }

    /**
     * 对象转 Short
     * @param obj 待转换的对象
     * @return 转换后的 Short 值，如果转换失败，则使用 toDouble().toInt().toShort()
     */
    @JvmStatic
    fun parseShort(obj: Any?): Short {
        return valueOf(obj).toShort()
    }

    /**
     * 对象转 Short (指定进制)
     * @param obj 待转换的对象
     * @param radix 进制
     * @return 转换后的 Short 值，如果转换失败，则使用 toDouble().toInt().toShort()
     */
    @JvmStatic
    fun parseShort(obj: Any?, radix: Int): Short {
        return valueOf(obj).toShort(radix)
    }

    /**
     * 对象转 Int (指定进制)
     * @param obj 待转换的对象
     * @param radix 进制
     * @return 转换后的 Int 值，如果转换失败，则使用 toDouble().toInt().toShort().toInt()
     */
    @JvmStatic
    fun parseInt(obj: Any?, radix: Int): Int {
        return valueOf(obj).toInt(radix)
    }

    /**
     * 对象转 Int (默认十进制)
     * @param obj 待转换的对象
     * @return 转换后的 Int 值，如果转换失败，则使用 toDouble().toInt().toShort().toInt()
     */
    @JvmStatic
    fun parseInt(obj: Any?): Int {
        return valueOf(obj).toInt()
    }

    /**
     * 对象转 Long (指定进制)
     * @param obj 待转换的对象
     * @param radix 进制
     * @return 转换后的 Long 值，如果转换失败，则使用 toDouble().toInt().toShort().toLong()
     */
    @JvmStatic
    fun parseLong(obj: Any?, radix: Int): Long {
        return valueOf(obj).toLong(radix)
    }

    /**
     * 对象转 Long (默认十进制)
     * @param obj 待转换的对象
     * @return 转换后的 Long 值，如果转换失败，则使用 toDouble().toInt().toShort().toLong()
     */
    @JvmStatic
    fun parseLong(obj: Any?): Long {
        return valueOf(obj).toLong()
    }

    /**
     * 对象转 Unsigned Long (指定进制)
     * @param obj 待转换的对象
     * @param radix 进制
     * @return 转换后的 Unsigned Long 值，如果转换失败，则使用 toLong( radix ).toShort().toLong()
     */
    @RequiresApi(api = Build.VERSION_CODES.O)
    @JvmStatic
    fun parseUnsignedLong(obj: Any?, radix: Int): Long {
        return valueOf(obj).toUnsignedLong(radix)
    }

    /**
     * 对象转 Unsigned Long (默认十进制)
     * @param obj 待转换的对象
     * @return 转换后的 Unsigned Long 值，如果转换失败，则使用 toLong( radix ).toShort().toLong()
     */
    @RequiresApi(api = Build.VERSION_CODES.O)
    @JvmStatic
    fun parseUnsignedLong(obj: Any?): Long {
        return valueOf(obj).toUnsignedLong()
    }

    /**
     * 对象转 Float
     * @param obj 待转换的对象
     * @return 转换后的 Float 值，如果转换失败，则使用 toDouble().toFloat()
     */
    @JvmStatic
    fun parseFloat(obj: Any?): Float {
        return valueOf(obj).toFloat()
    }

    /**
     * 对象转 Char 数组
     * @param obj 待转换的对象
     * @return 转换后的 Char 数组
     */
    @JvmStatic
    fun parseChars(obj: Any?): CharArray {
        return valueOf(obj).toChars()
    }

    /**
     * 对象转 Char
     * @param obj 待转换的对象
     * @return 转换后的 Char 值，如果对象为 null，则返回 null
     */
    @JvmStatic
    fun parseChar(obj: Any?): Char? {
        return valueOf(obj).toChar()
    }

    /**
     * 对象转 Boolean
     * @param obj 待转换的对象
     * @return 转换后的 Boolean 值
     */
    @JvmStatic
    fun parseBoolean(obj: Any?): Boolean {
        return valueOf(obj).toBoolean()
    }

    /**
     * 对象转数字字符串
     * @param obj 待转换的对象
     * @return 转换后的数字字符串，如果转换失败，则抛出 NumberFormatException
     */
    @JvmStatic
    fun parseNumberString(obj: Any?): String {
        return valueOf(obj).toNumber()
    }

    /**
     * EditText 控件增加对小数点的检查
     *
     * 例如：在 `addTextChangedListener`  中使用：
     * ```kotlin
     * editText.addTextChangedListener(object : TextWatcher {
     *     override fun afterTextChanged(s: Editable?) {
     *         ObjUtils.afterTextChangedOfCheckDecimal(editText, s, this, 16, 2)
     *     }
     *     // ... 其他方法
     * })
     * ```
     *
     * @param et 当前 EditText
     * @param edit `afterTextChanged`  方法的参数 Editable
     * @param tw 处于 `addTextChangedListener` 接口下时为 `this`
     * @param maxLength 允许输入的最大长度
     * @param decimalCount 小数点位数
     */
    @JvmStatic
    fun afterTextChangedOfCheckDecimal(
        et: EditText?,
        edit: Editable?,
        tw: TextWatcher?,
        maxLength: Int,
        decimalCount: Int
    ) {
        if ( et == null || edit == null || tw == null ) return
        try {
            var s = edit.toString()
            val sPeriod = PERIOD.toString()
            val sZero = "0"
            et.removeTextChangedListener( tw )
            // 限制最大长度
            et.filters = arrayOf( InputFilter.LengthFilter( maxLength ) )
            // 超过小数位限定位数, 只保留限定小数位数
            val periodIndex = s.indexOf( sPeriod )
            if ( periodIndex > -1 && s.length - periodIndex - 1 > decimalCount ) {
                s = s.substring( 0, periodIndex + decimalCount + 1 )
                edit.replace( 0, edit.length, s.trim() )
            }
            // 如果首位输入 "." 自动补 0
            if ( s.trim() == sPeriod ) {
                s = sZero + s
                edit.replace( 0, edit.length, s.trim() )
            }
            // 首位输入 0 时且长度超过 2 时，首位 0 抹除掉
            if ( s.startsWith( sZero ) && s.trim().length > 1 && !s.startsWith( sPeriod, 1 ) ) {
                edit.replace( 0, 1, "" )
            }
            et.addTextChangedListener( tw )
        } catch ( e: Exception ) {
            e.printStackTrace()
        }
    }

    /**
     * EditText 控件增加对小数点的检查（最大长度默认：16，小数点默认：2 位）
     *
     * 例如：在 `addTextChangedListener`  中使用：
     * ```kotlin
     * editText.addTextChangedListener(object : TextWatcher {
     *     override fun afterTextChanged(s: Editable?) {
     *         ObjUtils.afterTextChangedOfCheckDecimal(editText, s, this)
     *     }
     *     // ... 其他方法
     * })
     * ```
     *
     * @param et 当前 EditText
     * @param edit `afterTextChanged`  方法的参数 Editable
     * @param tw 处于 `addTextChangedListener` 接口下时为 `this`
     */
    @JvmStatic
    fun afterTextChangedOfCheckDecimal(et: EditText?, edit: Editable?, tw: TextWatcher?) {
        afterTextChangedOfCheckDecimal( et, edit, tw, 16, 2 )
    }

    /**
     * 传入数组 [0]、数组 [length - 1]、插入的数组，组合成一个完整数组
     * @param start 数组 [0] 的值
     * @param end 数组 [length - 1] 的值
     * @param centre 中间插入的数组
     * @return 完整数组
     */
    @JvmStatic
    fun toNewIntArrays(start: Int, end: Int, centre: IntArray? = null): IntArray {
        return if ( centre != null && centre.isNotEmpty() ) {
            val values = IntArray( centre.size + 2 )
            values[ 0 ] = start
            values[ values.size - 1 ] = end
            System.arraycopy( centre, 0, values, 1, centre.size )
            values
        } else {
            intArrayOf( start, end )
        }
    }

    /**
     * 安全的截取字符串（截取包含表情符号的字符串，避免出现乱码问题）
     * @param text 文本
     * @param startIndex 起始位置
     * @param endIndex 结束位置
     * @return 截取文本
     */
    @JvmStatic
    fun substringSafe(text: String, startIndex: Int, endIndex: Int): String {
        // 获取字符串的代码点数量 (确保 endIndex 不超过字符串长度)
        val codePointCount = text.codePointCount( 0, min( endIndex, text.length ) )
        // 计算截取后的起始和结束索引
        val realStartIndex = text.offsetByCodePoints( 0, startIndex )
        val realEndIndex = text.offsetByCodePoints( 0, codePointCount )
        // 使用 substring 方法截取字符串
        return text.substring( realStartIndex, realEndIndex )
    }

    /**
     * 首行缩进
     */
    @JvmOverloads
    fun setFirstLineIndent(
        tv: TextView,
        text: SpannableString,
        indentEveryLine: Boolean = false,
        indentChars: Int = 2,
        updateText: Boolean = true,
    ): SpannableString {
        val indentPixels = indentChars * tv.textSize.toInt() // 预先计算缩进像素值

        if (indentEveryLine) {
            // 缩进每一行
            text.split("\n").fold(0) { currentPosition, line ->
                val lineEnd = currentPosition + line.length
                text.setSpan(
                    LeadingMarginSpan.Standard(
                        indentPixels, 0
                    ),
                    currentPosition,
                    lineEnd,
                    SpannableString.SPAN_EXCLUSIVE_EXCLUSIVE
                )
                lineEnd + 1 // 更新 currentPosition，跳过换行符
            }
        } else {
            // 只缩进第一行
            text.setSpan(
                LeadingMarginSpan.Standard( indentPixels, 0 ),
                0,
                text.indexOf('\n').takeIf { it != -1 } ?: text.length,
                SpannableString.SPAN_EXCLUSIVE_EXCLUSIVE
            )
        }
        if( updateText ) tv.text = text
        return text
    }

    /**
     * 首行缩进
     */
    @JvmOverloads
    fun setFirstLineIndent(
        tv: TextView,
        text: String,
        indentEveryLine: Boolean = false,
        indentChars: Int = 2,
        updateText: Boolean = true,
    ): SpannableString {
        return setFirstLineIndent( tv, SpannableString( text ), indentEveryLine, indentChars, updateText )
    }

    /**
     * 设置指定文本右对齐
     */
    @JvmOverloads
    fun setRightJustifiedText(
        tv: TextView,
        text: SpannableString = SpannableString( tv.text ),
        updateText: Boolean = true,
        vararg rightText: String
    ): SpannableString {
        if( rightText.isEmpty() ) return SpannableString( tv.text )
        // 落款右对齐
        rightText.forEach {
            val signatureStart = text.lastIndexOf( it )
            val signatureEnd = text.length
            val alignmentSpan = AlignmentSpan.Standard( Layout.Alignment.ALIGN_OPPOSITE )
            text.setSpan(
                alignmentSpan,
                signatureStart,
                signatureEnd,
                SpannableString.SPAN_EXCLUSIVE_EXCLUSIVE
            )
        }
        if( updateText ) tv.text = text
        return text
    }
}